﻿using System.Text;
using Microsoft.Extensions.Logging;

namespace Devonline.AspNetCore;

/// <summary>
/// 数据文件操作类服务
/// </summary>
/// <param name="logger">日志</param>
/// <param name="httpSetting">Http配置项</param>
public class DataFileService(ILogger<FileService> logger, HttpSetting httpSetting) : FileService(logger, httpSetting), IDataFileService, IFileService
{
    /// <summary>
    /// 文件扩展名
    /// </summary>
    protected string _fileExtension = AppSettings.DEFAULT_TEXT_FILE_EXTENSION;

    /// <summary>
    /// 写入数据到文件, 每个对象写一行, 此为大数据量写入处理方法
    /// 自动写入需重写数据对象的 ToString 方法, 否则需要在子类实现对象和字符串的转换方式
    /// </summary>
    /// <typeparam name="TEntitySet">写入的数据类型</typeparam>
    /// <param name="entitySets">写入的数据</param>
    /// <param name="fileName">文件名</param>
    /// <param name="converter">对象到数据行的转换方法, 传入参数分别是: 当前行字符串, 当前行号, 总行号</param>
    /// <returns>写入的总行数</returns>
    public virtual async Task<long> WriteAsync<TEntitySet>(IEnumerable<TEntitySet> entitySets, string? fileName = default, Func<TEntitySet, long, long, string?>? converter = default) where TEntitySet : class
    {
        var index = 0L;
        if (!entitySets.Any())
        {
            return index;
        }

        var typeName = typeof(TEntitySet).GetDisplayName();
        var total = entitySets.LongCount();
        fileName = GetFileName<TEntitySet>(fileName);
        _logger.LogInformation($"将写入 {typeName} 数据 {total} 行到文件: {fileName} 中!");

        var line = string.Empty;
        using var writer = GetWriter(fileName);

        try
        {
            line = string.Empty;
            foreach (var entitySet in entitySets)
            {
                line = (converter ?? ConvertTo)(entitySet, index, total);
                if (!string.IsNullOrWhiteSpace(line))
                {
                    await writer.WriteLineAsync(line);
                }
            }

            _logger.LogInformation($"已写入 {typeName} 数据 {index} 行到文件: {fileName} 中!");
        }
        catch (Exception ex)
        {
            throw new Exception($"写入 {typeName} 数据第 {index} 行到文件: {fileName} 发生异常, 错误对象: {line}", ex);
        }
        finally
        {
            await writer.FlushAsync();
            writer.Close();
        }

        return index;
    }
    /// <summary>
    /// 从文件读取数据, 每行读取到一个对象, 此为大数据量读取处理方法
    /// </summary>
    /// <typeparam name="TEntitySet">读取的数据类型</typeparam>
    /// <param name="fileName">文件名</param>
    /// <param name="converter">数据行到对象的转换方法, 传入参数分别是: 当前行字符串, 当前行号</param>
    /// <returns>读取的数据</returns>
    public virtual async Task<List<TEntitySet>> ReadAsync<TEntitySet>(string? fileName = default, Func<string, long, TEntitySet?>? converter = default) where TEntitySet : class, new()
    {
        fileName = GetFileName<TEntitySet>(fileName);
        var typeName = typeof(TEntitySet).GetDisplayName();
        _logger.LogInformation($"将从文件: {fileName} 中读取 {typeName} 数据!");

        var index = 0L;
        var line = string.Empty;
        var entitySets = new List<TEntitySet>();
        using var reader = GetReader(fileName);

        try
        {
            while (!reader.EndOfStream)
            {
                line = await reader.ReadLineAsync();
                if (!string.IsNullOrWhiteSpace(line))
                {
                    var entitySet = (converter ?? ConvertTo<TEntitySet>)(line, index);
                    if (entitySet is not null)
                    {
                        entitySets.Add(entitySet);
                    }
                }
            }

            _logger.LogInformation($"已从文件: {fileName} 中读取 {index} 行 {typeName} 数据!");
        }
        catch (Exception ex)
        {
            throw new Exception($"从文件: {fileName} 读取第 {index} 行 {typeName} 数据发生异常, 错误行: {line}", ex);
        }
        finally
        {
            reader.Close();
        }

        return entitySets;
    }

    /// <summary>
    /// 转换对象为字符串形式
    /// </summary>
    /// <typeparam name="TEntitySet">对象类型</typeparam>
    /// <param name="entitySet">对象值</param>
    /// <param name="index">当前行数</param>
    /// <param name="total">总行数</param>
    /// <returns>转换后的字符串格式</returns>
    protected virtual string? ConvertTo<TEntitySet>(TEntitySet entitySet, long index, long total) where TEntitySet : class => throw new NotImplementedException();
    /// <summary>
    /// 转换字符串为指定对象
    /// </summary>
    /// <typeparam name="TEntitySet">对象类型</typeparam>
    /// <param name="value">字符串值</param>
    /// <param name="index">当前行数, 指有效行数</param>
    /// <returns>转换后的对象</returns>
    /// <exception cref="NotImplementedException"></exception>
    protected virtual TEntitySet? ConvertTo<TEntitySet>(string value, long index) where TEntitySet : class, new() => throw new NotImplementedException();
    /// <summary>
    /// 从文件名或类型获取/创建文件
    /// </summary>
    /// <typeparam name="TEntitySet">数据类型</typeparam>
    /// <param name="fileName">文件名</param>
    /// <returns>获取/创建的文件名</returns>
    /// <exception cref="DirectoryNotFoundException"></exception>
    protected virtual string GetFileName<TEntitySet>(string? fileName = default)
    {
        fileName ??= typeof(TEntitySet).Name + _fileExtension;
        fileName = GetAttachmentPath(fileName);
        var path = Path.GetDirectoryName(fileName);
        if (string.IsNullOrWhiteSpace(path))
        {
            throw new DirectoryNotFoundException($"文件: {fileName} 包含不正确的路径名!");
        }

        if (!Directory.Exists(path))
        {
            _logger.LogInformation($"文件路径 {path} 不存在, 将创建此文件路径!");
            Directory.CreateDirectory(path);
        }

        return fileName;
    }
    /// <summary>
    /// 获取或创建文本文件写入流
    /// </summary>
    /// <param name="fileName">文件名</param>
    /// <returns></returns>
    protected virtual StreamWriter GetWriter(string fileName)
    {
        if (!File.Exists(fileName))
        {
            return File.CreateText(fileName);
        }
        else
        {
            return new StreamWriter(fileName, true, Encoding.UTF8);
        }
    }
    /// <summary>
    /// 创建文本文件读取流
    /// </summary>
    /// <param name="fileName">文件名</param>
    /// <returns></returns>
    protected virtual StreamReader GetReader(string fileName)
    {
        if (File.Exists(fileName))
        {
            return new StreamReader(fileName, Encoding.UTF8);
        }
        else
        {
            throw new FileNotFoundException($"文件不存在", fileName);
        }
    }
}